/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#include "s_local.h"


static emitter_t *	s_emitters;

static emitter_t	s_activeEmitter;
static emitter_t *	s_freeEmitter;


/*
 ==================
 S_AddSoundEmitter
 ==================
*/
int S_AddSoundEmitter (int listenerId, const odVec3 &position, const odVec3 &velocity, bool underwater, int area){

	emitter_t	*emitter;

	if (!s_freeEmitter){
		Com_Printf(S_COLOR_YELLOW "S_AddSoundEmitter: no free emitter slots\n");
		return -1;
	}

	emitter = s_freeEmitter;

	// Fill it in
	emitter->active = true;

	emitter->listenerId = listenerId;
	emitter->position.Set(position.y, position.z, -position.x);
	emitter->velocity.Set(velocity.y, velocity.z, -velocity.x);
	emitter->underwater = underwater;
	emitter->area = area;

	// Link
	s_freeEmitter = s_freeEmitter->next;

	emitter->prev = &s_activeEmitter;
	emitter->next = s_activeEmitter.next;

	s_activeEmitter.next->prev = emitter;
	s_activeEmitter.next = emitter;

	return emitter->index;
}

/*
 ==================
 S_UpdateSoundEmitter
 ==================
*/
void S_UpdateSoundEmitter (int emitterHandle, int listenerId, const odVec3 &position, const odVec3 &velocity, bool underwater, int area){

	emitter_t	*emitter;

	if (emitterHandle < 0 || emitterHandle >= MAX_SOUND_EMITTERS)
		Com_Error(false, "S_UpdateSoundEmitter: emitterHandle out of range");

	emitter = &s_emitters[emitterHandle];

	if (!emitter->active){
		Com_Printf(S_COLOR_YELLOW "S_UpdateSoundEmitter: emitterHandle %i is not active\n", emitterHandle);
		return;
	}

	if (s_skipUpdates->integerValue)
		return;

	snd.pc.emitterUpdates++;

	// Update it
	emitter->listenerId = listenerId;
	emitter->position.Set(position.y, position.z, -position.x);
	emitter->velocity.Set(velocity.y, velocity.z, -velocity.x);
	emitter->underwater = underwater;
	emitter->area = area;
}

/*
 ==================
 S_FreeSoundEmitter
 ==================
*/
void S_FreeSoundEmitter (int emitterHandle, bool stopSounds){

	emitter_t	*emitter;
	channel_t	*channel;
	int			i;

	if (emitterHandle < 0 || emitterHandle >= MAX_SOUND_EMITTERS)
		Com_Error(false, "S_FreeSoundEmitter: emitterHandle out of range");

	emitter = &s_emitters[emitterHandle];

	if (!emitter->active){
		Com_Printf(S_COLOR_YELLOW "S_FreeSoundEmitter: emitterHandle %i is not active\n", emitterHandle);
		return;
	}

	// Stop all sounds on this emitter if desired. Otherwise let the channels
	// know that this emitter is no longer active.
	for (i = 0, channel = snd.channels; i < snd.numChannels; i++, channel++){
		if (!channel->sound)
			continue;

		if (channel->emitter != emitter)
			continue;

		if (stopSounds){
			S_StopChannel(channel);
			continue;
		}

		channel->emitter = NULL;
	}

	// No longer active
	emitter->active = false;

	// Unlink
	emitter->prev->next = emitter->next;
	emitter->next->prev = emitter->prev;

	emitter->next = s_freeEmitter;
	s_freeEmitter = emitter;
}


// ============================================================================


/*
 ==================
 S_PlaySoundOnEmitter
 ==================
*/
int S_PlaySoundOnEmitter (int emitterHandle, int channelId, soundShader_t *soundShader){

	emitter_t	*emitter;
	channel_t	*channel;

	if (emitterHandle < 0 || emitterHandle >= MAX_SOUND_EMITTERS)
		Com_Error(false, "S_PlaySoundOnEmitter: emitterHandle out of range");

	emitter = &s_emitters[emitterHandle];

	if (!emitter->active){
		Com_Printf(S_COLOR_YELLOW "S_PlaySoundOnEmitter: emitterHandle %i is not active\n", emitterHandle);
		return 0;
	}

	if (s_showEmitters->integerValue)
		Com_Printf("S_PlaySoundOnEmitter( %i, %i, %s )\n", emitterHandle, channelId, soundShader->name);

	// Handle private and anti-private sounds
	if (soundShader->flags & SSF_PRIVATE){
		if (emitter->listenerId != snd.listener.listenerId)
			return 0;
	}
	if (soundShader->flags & SSF_ANTIPRIVATE){
		if (emitter->listenerId == snd.listener.listenerId)
			return 0;
	}

	// Pick a channel and play the sound
	channel = S_PickChannel(emitter->listenerId, channelId, soundShader, false, emitter);
	if (!channel)
		return 0;

	S_PlayChannel(channel);

	return channel->sound->length;
}

/*
 ==================
 S_StopSoundOnEmitter
 ==================
*/
void S_StopSoundOnEmitter (int emitterHandle, int channelId){

	emitter_t	*emitter;
	channel_t	*channel;
	int			i;

	if (emitterHandle < 0 || emitterHandle >= MAX_SOUND_EMITTERS)
		Com_Error(false, "S_StopSoundOnEmitter: emitterHandle out of range");

	emitter = &s_emitters[emitterHandle];

	if (!emitter->active){
		Com_Printf(S_COLOR_YELLOW "S_StopSoundOnEmitter: emitterHandle %i is not active\n", emitterHandle);
		return;
	}

	if (s_showEmitters->integerValue)
		Com_Printf("S_StopSoundOnEmitter( %i, %i )\n", emitterHandle, channelId);

	// Stop all sounds on the given channel
	for (i = 0, channel = snd.channels; i < snd.numChannels; i++, channel++){
		if (!channel->sound)
			continue;

		if (channel->emitter != emitter)
			continue;

		if (channelId == SOUND_CHANNEL_ANY || channel->channelId == channelId)
			S_StopChannel(channel);
	}
}


// ============================================================================


/*
 ==================
 S_InitEmitters
 ==================
*/
void S_InitEmitters (void){

	int		i;

	// Allocate and set up the emitters
	s_emitters = (emitter_t *)Mem_ClearedAlloc(MAX_SOUND_EMITTERS * sizeof(emitter_t), TAG_SOUND);

	for (i = 0; i < MAX_SOUND_EMITTERS; i++){
		s_emitters[i].index = i;

		if (i < MAX_SOUND_EMITTERS - 1)
			s_emitters[i].next = &s_emitters[i+1];
	}

	s_activeEmitter.prev = &s_activeEmitter;
	s_activeEmitter.next = &s_activeEmitter;

	s_freeEmitter = s_emitters;
}

/*
 ==================
 S_ShutdownEmitters
 ==================
*/
void S_ShutdownEmitters (void){

	emitter_t	*emitter;
	channel_t	*channel;
	int			i;

	// Stop all the emitters
	for (emitter = s_activeEmitter.next; emitter != &s_activeEmitter; emitter = emitter->next){
		// Stop all sounds on this emitter
		for (i = 0, channel = snd.channels; i < snd.numChannels; i++, channel++){
			if (!channel->sound)
				continue;

			if (channel->emitter != emitter)
				continue;

			S_StopChannel(channel);
		}
	}

	// Clear emitter list
	s_activeEmitter.prev = &s_activeEmitter;
	s_activeEmitter.next = &s_activeEmitter;

	s_freeEmitter = NULL;
}
